home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / presto / presto10.lha / src / scheduler.C < prev    next >
C/C++ Source or Header  |  1991-12-11  |  11KB  |  503 lines

  1.  
  2. /*
  3.  * scheduler.c
  4.  *
  5.  *    Implementation of shared scheduler class.  All process
  6.  *    objects interact with the scheduler object trying to
  7.  *    getreadythreads off the ready queue.
  8.  *
  9.  *    ThreadPool trivially defines the base class for the shared
  10.  *    ready thread pool.
  11.  *
  12.  *    ThreadPoolQueue defines the default scheduling discipline.  
  13.  *    Very simple, no priorities.... nothing fancy.
  14.  *
  15.  *      Last modified:          06/19/91
  16.  *      by:                     pbd
  17.  *      reason:                 Reversed order in which affinity is
  18.  *                              set. Previous method was to set affinity,
  19.  *                              and then fork children. New method forks
  20.  *                              children, and then sets affinity. 
  21.  *                              Hence, new code in Scheduler::invoke that
  22.  *                              used to be in Process::Process (the
  23.  *                              ctor for the root process.
  24.  *
  25.  *      Last modified:          11/07/89
  26.  *      by:                     jef
  27.  *      reason:                 Fix problem when numprocessors > NUMPROCS.
  28.  *                              This caused PRESTO to write right off the
  29.  *                              end of sc_p_numprocs array, trashing
  30.  *                              sc_p_numschedulers field, causing huge
  31.  *                              numbers of processes to be created, using
  32.  *                              up all job slots on system.
  33.  *
  34.  *    Last modified:        1/1/88
  35.  *    by:            bnb
  36.  *    reason:            take care for preschedthreads
  37.  *
  38.  *    Last modified:        12/21/87
  39.  *    by:            bnb
  40.  *    reason:            add these comments
  41.  *
  42.  */
  43.  
  44. #define _SCHEDULER_C
  45.  
  46. #include <stddef.h>
  47. #include <sys/types.h>
  48. #include <signal.h>
  49. #include <osfcn.h>
  50. #include <stream.h>
  51. #include "presto.h"
  52.  
  53.  
  54. extern Main *MAIN;
  55.  
  56. //
  57. // preschedthreads queue is where threads that get created
  58. // before the scheduler exists get placed.  On scheduler
  59. // creation, these guys are moved to the real thread pool.
  60. // This allows threads to be created in static constructors without
  61. // requiring that the entire system be in place.
  62. //
  63.  
  64.  
  65. shared_t ThreadQ    *preschedthreads = 0;
  66.  
  67.  
  68. //
  69. // Definition of default ThreadPool{Queue} type known to scheduler.
  70. //
  71.  
  72.  
  73. ThreadPool::ThreadPool()
  74. {
  75. }
  76.  
  77. ThreadPool::~ThreadPool()
  78. {
  79. }
  80.  
  81. Thread*
  82. ThreadPool::get()
  83. {
  84.     error("Base threadpool get");
  85.     // NOTREACHED
  86.     return 0;
  87. }
  88.  
  89. void
  90. ThreadPool::insert(Thread* t)
  91. {
  92.     error("Base threadpool get");
  93.     // NOTREACHED
  94.     t = t;        // Satisfy cfront
  95. }
  96.  
  97. int
  98. ThreadPool::size()
  99. {
  100.     error("Base threadpool size");
  101.     // NOTREACHED
  102.     return 1;
  103. }
  104.  
  105.  
  106.  
  107. ThreadPoolQueue::ThreadPoolQueue()
  108. {
  109.     tp_tq = new ThreadQ(TS_READY);
  110. }
  111.  
  112. ThreadPoolQueue::~ThreadPoolQueue()
  113. {
  114.     delete tp_tq;
  115. }
  116.  
  117.  
  118. Thread*
  119. ThreadPoolQueue::get()
  120. {
  121.     register ThreadQ* tq = tp_tq;        // speed hack
  122.     return tq->get();
  123. }
  124.  
  125. void
  126. ThreadPoolQueue::insert(Thread* t)
  127. {
  128.     register ThreadQ* tq = tp_tq;
  129.     tq->append(t);
  130. }
  131.  
  132. int
  133. ThreadPoolQueue::size()
  134. {
  135.     return tp_tq->length();
  136. }
  137.  
  138.  
  139.  
  140.  
  141. //
  142. // The main scheduler system
  143. //
  144.  
  145. Scheduler::Scheduler(int numschedulers, int quantum)
  146. {
  147. #ifdef PREEMPT
  148.     void sigpreempt_init();
  149. #endif /* PREEMPT */
  150.  
  151.         //
  152.         //  Make sure user-specified numprocessors isn't more than PRESTO
  153.         //  can support.
  154.         //
  155.         if (numschedulers > NUMPROCS) {
  156.             sc_p_numschedulers = NUMPROCS;
  157.             cerr << "'numprocessors' of " << numschedulers << " too big - ";
  158.             cerr << "using " << NUMPROCS << "\n";
  159.         }
  160.         else
  161.            sc_p_numschedulers = numschedulers;
  162.  
  163. #ifdef DEBUG_STARTUP
  164.         cout << "making scheduler, sc_p_numschedulers = "
  165.              <<sc_p_numschedulers<<"\n";
  166. #endif /* DEBUG_STARTUP */
  167.  
  168.     //
  169.     // nullify the threads
  170.     //
  171.         for (int jj=0; jj<sc_p_numschedulers; jj++)
  172.             sc_t_ready [jj] = new ThreadPoolQueue;
  173.  
  174.     //
  175.     // Only create a process context if one doesn't already exist.
  176.     // User can create his own in Main::init().  Allows us to
  177.     // virtualize the constructor.  
  178.     //
  179.     if (thisproc == 0)
  180.         sc_p_procs[0] = new Process(P_ROOT, 0);
  181.     else
  182.         sc_p_procs[0] = thisproc;
  183.  
  184.     
  185.     sc_p_activeschedulers = 1;
  186.     sc_p_busybits = 0x0;
  187.     sc_lock = new Spinlock;
  188.     sc_quantum = quantum;
  189.  
  190. #ifdef PREEMPT
  191.     if (sc_quantum)
  192.         sigpreempt_init();
  193. #endif /* PREEMPT */
  194.  
  195. #ifdef DEBUG_STARTUP
  196.     cout << "done making scheduler\n";
  197. #endif /* DEBUG_STARTUP */
  198. }
  199.  
  200. //
  201. // Scheduler invocation function.
  202. //     Create sc_p_numschedulers threads and bind each one of them
  203. //    to a process invocation.  Schedule those threads to start running
  204. //    which will in turn run a new scheduler on some new processor.
  205. //
  206. //    In case we have any preschedthreads, schedule them here.
  207. //
  208.  
  209. int
  210. Scheduler::invoke()
  211. {
  212.     int cpusonline = cpus_online();
  213.     int pid;
  214.  
  215. #ifdef DEBUG_STARTUP
  216.         cout << "scheduler_starter thread - in sched->invoke ()\n";
  217. #endif /* DEBUG_STARTUP */
  218.  
  219. #ifdef PREEMPT
  220.     extern void sigpreempt_beginclock(struct timeval *q);
  221.     extern int preemption_enabled;
  222. #endif /* PREEMPT */
  223.  
  224.     if (sc_p_numschedulers < 0)
  225.         return sc_p_activeschedulers;
  226.  
  227.     if (sc_p_numschedulers > cpusonline)
  228.       sc_p_numschedulers = cpusonline;
  229.  
  230.     //
  231.     // Initialize the process pool
  232.     //
  233.  
  234.     initsighandlers(0);
  235.  
  236.     for (pid = sc_p_activeschedulers;
  237.          pid < sc_p_numschedulers; pid++)
  238.     {
  239.  
  240. #define XNAME "proc_X"
  241.         char *pname = new char[sizeof(XNAME)];
  242.         strcpy(pname, XNAME);
  243.         pname[5] = pid + 'a' - 1;    
  244.         sc_p_procs[pid] = thisproc->newprocess(pname,pid+thisproc->id());
  245.         if (sc_p_procs[pid]->state() & S_ERROR)    {
  246.             perror("Scheduler::new Process");
  247.             sc_p_numschedulers = --pid;
  248.             this->abort(SIGKILL);
  249.             kill(getpid(), SIGILL);
  250.             // not reached
  251.         }
  252.         sc_p_busybits |= 1<<pid;    // assume busy
  253.     } 
  254.  
  255. #ifdef sequent
  256. #ifdef i386
  257.  
  258.         //
  259.         // Set processor affinity if applicable.
  260.         // This takes effect for the scheduler processes only,
  261.     // all other thread processes having had their affinity
  262.     // set (if applicable) in Process::p_runchild()
  263.         //
  264.  
  265.         if (MAIN->get_affinity ())
  266.         {
  267.             if (tmp_affinity (thisproc->id()) == -1)
  268.                 cout << "unable to set affinity on root process "
  269.                      << thisproc->id() << "\n";
  270. //            else
  271. //                cout << "affinity set on root process "
  272. //             << thisproc->pid()
  273. //                     << thisproc->id() << "\n";
  274. //            cout.flush ();
  275.         }
  276. #endif /* i386 */
  277. #endif /* sequent */
  278.  
  279.     initsighandlers(1);    // prepare parent to clean up
  280. #ifdef PREEMPT
  281.     if (sc_quantum)    {
  282.         struct timeval    q;
  283.         q.tv_sec = sc_quantum / 1000;           // in msecs
  284.         q.tv_usec = (sc_quantum % 1000) * 1000; // 1msec = 1000usec
  285.         sigpreempt_beginclock(&q);
  286.     }    
  287. #endif /* PREEMPT */
  288.  
  289.     //
  290.     // take care of threads created in the prescheduler era
  291.     //
  292.  
  293.     if (preschedthreads)    {
  294.         Thread *t;
  295.         while (t=preschedthreads->get())
  296.             resume(t);
  297.         delete preschedthreads;
  298.         preschedthreads = 0;
  299.     }
  300.  
  301. #ifdef DEBUG_STARTUP
  302.         cout << "done with invoke() \n";
  303. #endif /* DEBUG_STARTUP */
  304.  
  305.     return (sc_p_activeschedulers = sc_p_numschedulers);
  306. }
  307.  
  308.  
  309. Scheduler::~Scheduler()
  310. {
  311.     for (int j=0; j<sc_p_numschedulers; j++)
  312.     delete sc_t_ready [j];
  313. }
  314.  
  315.  
  316. void
  317. Scheduler::halt()
  318. {
  319.     int pid;
  320.  
  321. #ifdef PREEMPT
  322.     extern void sigpreempt_stopclock();
  323.  
  324.     if (sc_quantum)
  325.         sigpreempt_stopclock();
  326. #endif /* PREEMPT */
  327. #ifdef PROFILE
  328.     QFinish();
  329. #endif
  330.  
  331.     /*
  332.      * Stop listening for dead children -- otherwise can race with "master"
  333.      * and get spurious "exit" messages from schedulerReapChild().
  334.      */
  335.  
  336.     initsighandlers(-1);                // turn off SIGCHLD
  337.  
  338.     /*
  339.      * Ask the kids to quietly die.
  340.      */
  341.  
  342.     for (pid = 1; pid < sc_p_numschedulers; pid++)
  343.         sc_p_procs[pid]->request( R_RETURN /*R_DIE*/);
  344.  
  345.     sc_p_procs[0]->request(R_RETURN);    // cause main to return
  346. }
  347.  
  348.  
  349.  
  350. //
  351. // Resume a thread within a process.
  352. // If this is a virgin thread, then the thread code starts off a little
  353. // but differently.  See threads.c
  354. //
  355.  
  356. void
  357. Scheduler::resume(Thread *t)
  358. {
  359.     if (t->flags()&TF_SCHEDULER)
  360.         t->error("Can't resume a scheduler thread\n");
  361.     t->isready();
  362.     sc_t_ready [thisproc->id()]->insert(t);    // some process should grab me
  363. }
  364.  
  365.  
  366.  
  367.  
  368. //
  369. // Have to serialize access here so we can know when the system stops.
  370. // Should really be more sophisticated as to determining when everything
  371. // is done.
  372. //
  373.  
  374. Thread*
  375. Scheduler::getreadythread()
  376. {
  377.     Thread *t;
  378.     int id, my_id=thisproc->id();
  379.  
  380.     //
  381.     //  Look in our own readyq first.
  382.     //  If no ready threads in our readyq, try everyone elses.
  383.     //
  384.     t = sc_t_ready [my_id]->get();
  385.  
  386. //if (t) cout << my_id << " - ready thread in my own queue\n";
  387.     id = my_id + 1;
  388.     while (!t)
  389.     {
  390.         if (id == sc_p_numschedulers) id = 0;   // wrap if necessary
  391.         if (id == my_id) break;                 // back where we started
  392.         t = sc_t_ready [id]->get();             // try this process
  393. //if (t) cout << my_id << " - ready thread in " << id << "'s queue\n";
  394.         id++;
  395.     }
  396.  
  397.     if (t)    {
  398. #ifdef PROFILE
  399.             QThreadRun(t->getqThread());
  400. #endif
  401.                 sc_lock->lock();
  402.         (void)busybits(1);
  403.         sc_lock->unlock();
  404.     } else    {
  405.                 sc_lock->lock();
  406.         int busy = busybits(0);
  407.         sc_lock->unlock();        
  408.  
  409. //cout << my_id << " - no ready threads in any queue!!\n";
  410.         //
  411.         // If no busy procs, and we are the master....
  412.         //
  413.         if (busy == 0 && thisproc->isroot() )
  414.             this->halt();
  415.     }
  416.  
  417.     return t;
  418. }
  419.  
  420.  
  421. void
  422. Scheduler::error(char *s)
  423. {
  424.     cerr << "Scheduler error " << s << "\n" << " in " << hex(long(this)) << "\n";
  425.     fatalerror();
  426. }
  427.  
  428.  
  429. ThreadPool*
  430. Scheduler::getreadypool()
  431. {
  432.     return sc_t_ready [thisproc->id()]; 
  433. }
  434.  
  435.  
  436. //
  437. // swap new ready pool.  Order of switch is important here.
  438. //    First, replace old pool with new pool.
  439. //    Then,  move any threads from old pool to new pool
  440. //    Them,  delete old pool.
  441. //
  442.  
  443. ThreadPool*
  444. Scheduler::setreadypool(ThreadPool* newtp)
  445. {
  446.     ThreadPool* oldpool ;
  447.     Thread*    t;
  448.  
  449.     if (!newtp)
  450.         error("Bad arg to newreadypool");
  451.  
  452.     oldpool = sc_t_ready[thisproc->id()];    // can still be active
  453.     sc_t_ready[thisproc->id()] = newtp;
  454.                     // we expect to have only reference to
  455.                     // oldpool by this point.
  456.     if (oldpool)    {
  457.         while (t=oldpool->get())
  458.             sc_t_ready[thisproc->id()]->insert(t);
  459.     }
  460.  
  461.     return oldpool;
  462. }
  463.  
  464. void
  465. Scheduler::print(ostream& s)
  466. {
  467.     int i;
  468.  
  469.     s << form("(Scheduler):0x%x:\n", this);
  470.     for (i = 0; i < sc_p_activeschedulers; i++)
  471.         s << form ("sc_t_ready[%d]=0x%x, ", i, sc_t_ready[i]);
  472.     s << form("sc_p_activescheduler=%d, sc_p_busybits=%d, sc_quantum=%d",
  473.             sc_p_activeschedulers, sc_p_busybits, sc_quantum);
  474.     s << sc_lock << "\n";
  475.     for (i = 0; i < sc_p_activeschedulers; i++)
  476.         s << sc_p_procs[i] << "\n";
  477. }
  478.  
  479. //
  480. // global version of abort.  If we are the scheduler, then knock
  481. // everyone else off first, otherwise just knock ourselves off and
  482. // reply on the scheduler to pick us up.
  483. //
  484. #ifdef __DECCXX
  485. void
  486. #else
  487. int
  488. #endif
  489. abort( void )
  490. {
  491.     if (sched)    {
  492.         sched->abort(SIGILL);
  493.         // NOT REACHED
  494.     } else            
  495.         kill(getpid(), SIGILL);        // core dump
  496. #ifdef __DECCXX
  497.     return;                // not reached
  498. #else
  499.     return 0;             // not reached
  500. #endif /* __DECCXX */
  501. }
  502.  
  503.